import time
from machine import Pin, SPI, PWM
import framebuf

BL_PIN   = 13
DC_PIN   = 8
RST_PIN  = 12
MOSI_PIN = 11
SCK_PIN  = 10
CS_PIN   = 9


class LCD_0inch96(framebuf.FrameBuffer):
    def __init__(self):
        self.width = 160
        self.height = 80

        self.cs = Pin(CS_PIN, Pin.OUT)
        self.rst = Pin(RST_PIN, Pin.OUT)
        self.dc = Pin(DC_PIN, Pin.OUT)
        self.cs.value(1)

        self.spi = SPI(1, baudrate=10_000_000, polarity=0, phase=0,
                        sck=Pin(SCK_PIN), mosi=Pin(MOSI_PIN), miso=None)

        self.buffer = bytearray(self.width * self.height * 2)
        super().__init__(self.buffer, self.width, self.height, framebuf.RGB565)

        self._init_display()

    def _cmd(self, cmd):
        self.cs.value(0)
        self.dc.value(0)
        self.spi.write(bytearray([cmd]))
        self.cs.value(1)

    def _data(self, data):
        self.cs.value(0)
        self.dc.value(1)
        if isinstance(data, int):
            self.spi.write(bytearray([data]))
        else:
            self.spi.write(data)
        self.cs.value(1)

    def _backlight(self, value):
        pwm = PWM(Pin(BL_PIN))
        pwm.freq(1000)
        value = min(value, 1000)
        pwm.duty_u16(int(value * 65535 / 1000))

    def _init_display(self):
        self.rst.value(1); time.sleep_ms(200)
        self.rst.value(0); time.sleep_ms(200)
        self.rst.value(1); time.sleep_ms(200)

        self._backlight(1000)

        self._cmd(0x11)
        time.sleep_ms(120)

        self._cmd(0x21)

        self._cmd(0xB1)
        for d in (0x05, 0x3C, 0x3C): self._data(d)
        self._cmd(0xB2)
        for d in (0x05, 0x3C, 0x3C): self._data(d)
        self._cmd(0xB3)
        for d in (0x05, 0x3C, 0x3C, 0x05, 0x3C, 0x3C): self._data(d)

        self._cmd(0xB4)
        self._data(0x03)

        self._cmd(0xC0)
        for d in (0xAB, 0x0B, 0x04): self._data(d)
        self._cmd(0xC1)
        self._data(0xC5)
        self._cmd(0xC2)
        for d in (0x0D, 0x00): self._data(d)
        self._cmd(0xC3)
        for d in (0x8D, 0x6A): self._data(d)
        self._cmd(0xC4)
        for d in (0x8D, 0xEE): self._data(d)

        self._cmd(0xC5)
        self._data(0x0F)

        self._cmd(0xE0)
        for d in (0x07, 0x0E, 0x08, 0x07, 0x10, 0x07, 0x02, 0x07,
                  0x09, 0x0F, 0x25, 0x36, 0x00, 0x08, 0x04, 0x10):
            self._data(d)
        self._cmd(0xE1)
        for d in (0x0A, 0x0D, 0x08, 0x07, 0x0F, 0x07, 0x02, 0x07,
                  0x09, 0x0F, 0x25, 0x35, 0x00, 0x09, 0x04, 0x10):
            self._data(d)

        self._cmd(0xFC)
        for d in (0x11, 0x15): self._data(d)

        self._cmd(0x3A)
        self._data(0x05)

        self._cmd(0x36)
        self._data(0xA8)

        self._cmd(0x29)

    def show(self):
        x0, x1 = 1, 160
        y0, y1 = 26, 105

        self._cmd(0x2A)
        self._data(bytearray([0x00, x0, 0x00, x1]))
        self._cmd(0x2B)
        self._data(bytearray([0x00, y0, 0x00, y1]))
        self._cmd(0x2C)

        self.cs.value(0)
        self.dc.value(1)
        self.spi.write(self.buffer)
        self.cs.value(1)


RED = 0x00F8
WHITE = 0xFFFF
BLACK = 0x0000

lcd = LCD_0inch96()
lcd.fill(BLACK)

text = "Ardustore"
scale = 2
char_w = 8 * scale
total_w = char_w * len(text)
x = (lcd.width - total_w) // 2
y = (lcd.height - 8 * scale) // 2

for i, ch in enumerate(text):
    color = RED if i == 0 else WHITE
    cx = x + i * char_w
    glyph_buf = bytearray(8 * 2)
    glyph = framebuf.FrameBuffer(glyph_buf, 8, 8, framebuf.MONO_HLSB)
    glyph.text(ch, 0, 0, 1)
    for gy in range(8):
        for gx in range(8):
            if glyph.pixel(gx, gy):
                lcd.fill_rect(cx + gx * scale, y + gy * scale, scale, scale, color)

lcd.show()